<?php

/**
 * 
 * @see DataModel
 */
require_once LIB_PATH . 'DataModel.php';

/**
 * 
 * @see PhpClassFactory
 */
require_once LIB_PATH . 'PhpClassFactory.php';

/**
 * Project 
 * 
 * @uses DataModel
 * @package php-phoenix
 * @subpackage project
 * @version $id$
 * @author Pablo López Torres <pablolopeztorres@gmail.com> 
 */
class Project extends DataModel
{
    /**
     * PHP_FILE_PATTERN 
     * 
     * @const string Perl Regular Expression
     */
    const PHP_FILE_PATTERN = '/^.*\.php$/';

    /**
     * name 
     * 
     * @var mixed
     * @access private
     */
    private $name;

    /**
     * path 
     * 
     * @var mixed
     * @access private
     */
    private $path;

    /**
     * filenames 
     * 
     * @var array Array of strings with the filenames
     * @access private
     */
    private $filenames = array();

    /**
     * files 
     * 
     * @var array Array of strings with the filenames
     * @access private
     */
    private $files = array();


    /**
     * setName
     * 
     * @access public
     * @author Pablo López Torres <pablolopeztorres@gmail.com> 
     * @param mixed $name 
     * @return void
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * getName
     * 
     * @access public
     * @author Pablo López Torres <pablolopeztorres@gmail.com> 
     * @return void
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * setPath
     * 
     * @access public
     * @author Pablo López Torres <pablolopeztorres@gmail.com> 
     * @param mixed $path 
     * @return void
     */
    public function setPath($path)
    {
        if (! is_dir($path))
        {
            throw new DirectoryDoesNotExistException();
        }

		$path = realpath($path);

        $this->path = $path;
    }

    /**
     * getPath
     * 
     * @access public
     * @author Pablo López Torres <pablolopeztorres@gmail.com> 
     * @return void
     */
    public function getPath()
    {
        return $this->path;
    }

    /**
     * getPersistentKey
     * 
     * @access protected
     * @author Pablo López Torres <pablolopeztorres@gmail.com> 
     * @param string $element 
     * @return string
     */
    public function getPersistentKey($element)
    {
        return "project_$element";
    }


    //
    // Filename related
    //
    /**
     * addFilename
     * 
     * @access public
     * @author Pablo López Torres <pablolopeztorres@gmail.com> 
     * @param string $filename 
     * @return boolean Success
     */
    public function addFilename($filename, $ignorePhpFilePattern = FALSE)
    {
        if (! is_readable($filename))
        {
            throw new FilenameNotFoundException();
        }

		$filename = realpath($filename);

		$status = FALSE;
		if ($ignorePhpFilePattern || preg_match(self::PHP_FILE_PATTERN, $filename))
		{
			$projectPath = $this->getPath(); 
			if (substr($filename, 0, strlen($projectPath)) !== $projectPath)
			{
				throw new FilenameIsNotInValidPathException();
			}

			$fileRelativePath = substr($filename, strlen($projectPath));

			if (! isset($this->filenames[$fileRelativePath]))
			{
				$this->filenames[$fileRelativePath] = FALSE;
				ksort($this->filenames);
				$status = TRUE;
			}
			else
			{
				throw new FileWasAlreadyAddedException();
			}
		}

        return $status;
    }

    /**
     * removeFilename
     * 
     * @access public
     * @author Pablo López Torres <pablolopeztorres@gmail.com> 
     * @param string $filename 
     * @return boolean Success
     */
    public function removeFilename($filename)
    {
        $status = FALSE;
        if (isset($this->filenames[$filename]))
        {
            unset($this->filenames[$filename]);
            $status = TRUE;
        }

        return $status;
    }

    /**
     * getFilenamesStatus
     * 
     * @access public
     * @author Pablo López Torres <pablolopeztorres@gmail.com> 
     * @return void
     */
    public function getFilenamesStatus()
    {
        return $this->filenames;
    }

    /**
     * getFilenamesAsArray
     * 
     * @access public
     * @author Pablo López Torres <pablolopeztorres@gmail.com> 
     * @return void
     */
    public function getFilenames()
    {
        return array_keys($this->filenames);
    }

	/**
	 * indexFilename
	 * 
	 * @access public
	 * @author Pablo López Torres <pablolopeztorres@gmail.com> 
	 * @param mixed $filename 
	 * @param mixed $forceWrite 
	 * @return void
	 */
	public function indexFilename($filename, $forceWrite = FALSE)
	{
		if (! isset($this->filenames[$filename]))
		{
			throw new FileDoesNotBelongToProjectException($filename);
		}

        // load the catalog for the required project
        $catalogKey = $this->name . '/catalog';

        $catalog = Storage::load($catalogKey);

        if ($catalog === NULL)
        {
            $catalog = array(
                'methods' => array(),
                'classes' => array(),
            );
        }

		$tokens = Tokenizer::prepare(file_get_contents($this->path . $filename));

		$elements = array();
		do 
		{
			$token =& current($tokens);

			$element = Parser::fetchUnknownElement($tokens, $this->path . $filename, $this->name);

			if (isset($element))
			{
				$elements[] = $element;

				if ($element instanceof PhpClass)
				{
					$persistentKey = $element->getPersistentKey();

					// save it!!
					if (Storage::save($element, $persistentKey, $forceWrite))
					{
						print "Class '{$element->getName()}' has been added to the index.\n";

						$catalog['classes'][] = $element->getName();
					}
				}
			}
		}
		while (next($tokens));

        // saving catalog with the class names
        Storage::save($catalog, $catalogKey, TRUE);

		$this->filenames[$filename] = TRUE;
		ProjectFactory::getInstance()->save($this->name, TRUE); // override the previous one
	}


	//
	// Class related
	//
	/**
	 * getClassNames
	 * 
	 * @access public
	 * @author Pablo López Torres <pablolopeztorres@gmail.com> 
	 * @return void
	 */
	public function getClassNames() {
        $catalogKey = $this->name . '/catalog';
        $catalog = Storage::load($catalogKey);
		return $catalog['classes'];
	}

	public function getClasses() {
        $catalogKey = $this->name . '/catalog';
        $catalog = Storage::load($catalogKey);

		$classes = array();
		foreach ($catalog['classes'] as $className)
		{
			$classes[$className] = PhpClassFactory::getInstance()->load($this->name, $className);
		}

		return $classes;
	}
	public function getClass($className) {
		return PhpClassFactory::getInstance()->load($this->name, $className);
	}
}

/**
 * DirectoryDoesNotExistException 
 * 
 * @uses Exception
 * @author Pablo López Torres <pablolopeztorres@gmail.com> 
 */
class DirectoryDoesNotExistException extends Exception
{
}

/**
 * NotAnAbsolutePathException 
 * 
 * @uses Exception
 * @author Pablo López Torres <pablolopeztorres@gmail.com> 
 */
class NotAnAbsolutePathException extends Exception
{
}

/**
 * FilenameNotFoundException 
 * 
 * @uses Exception
 * @author Pablo López Torres <pablolopeztorres@gmail.com> 
 */
class FilenameNotFoundException extends Exception
{
}

/**
 * FilenameIsNotInValidPathException 
 * 
 * @uses Exception
 * @author Pablo López Torres <pablolopeztorres@gmail.com> 
 */
class FilenameIsNotInValidPathException extends Exception
{
}

/**
 * FileWasAlreadyAddedException 
 * 
 * @uses Exception
 * @author Pablo López Torres <pablolopeztorres@gmail.com> 
 */
class FileWasAlreadyAddedException extends Exception
{
}

